{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pL--_KGdYoBz"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "uBDvXpYzYnGj"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HQzaEQuJiW_d"
      },
      "source": [
        "# TFRecord dan tf.Example\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/load_data/tfrecord\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Lihat di TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/id/tutorials/load_data/tfrecord.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Jalankan di Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/id/tutorials/load_data/tfrecord.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Lihat source di GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/id/tutorials/load_data/tfrecord.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Unduh notebook</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3pkUd_9IZCFO"
      },
      "source": [
        "Membaca data secara efisien akan membantu untuk menjadikan data Anda terserialisasi dan menyimpannya dalam kumpulan file (100 hingga 200MB) yang dapat dibaca secara linear. Hal ini dapat dilakukan apabila data di kirimkan melalui jaringan. Hal ini juga dapat berguna untuk proses penyimpanan sementara setiap *data-preprocessing*.\n",
        "\n",
        "Format TFRecord adalah format sederhana untuk menyimpan urutan dari record biner.\n",
        "\n",
        "[Protocol buffers](https://developers.google.com/protocol-buffers/) adalah sebuah *library* antarplatform dan antarbahasa untuk menserialisasi data yang terstruktur secara efisien.\n",
        "\n",
        "Pesan protokol didefinisikan sebagai file `.proto`, ini sering menjadi cara termudah untuk memahami sebuah tipe pesan.\n",
        "\n",
        "Pesan `tf.Example` (atau protobuf) adalah tipe pesan yang fleksibel yang merepresentasikan pemetaan `{\"string\": value}`. Hal ini didesain untuk digunakan dengan Tensorflow dan digunakan diseluruh level API yang lebih tinggi seperti [TFX](https://www.tensorflow.org/tfx/)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ac83J0QxjhFt"
      },
      "source": [
        "This notebook will demonstrate how to create, parse, and use the `tf.Example` message, and then serialize, write, and read `tf.Example` messages to and from `.tfrecord` files.\n",
        "Notebook ini akan mendemonstrasikan bagaimana cara membuat, menguraikan, dan menggunakan pesan `tf.Example`, dan kemudian menserilaisasi, menulis, dan membaca pesan `tf.Example` kepada dan dari file `.tfrecord`.\n",
        "\n",
        "Note: While useful, these structures are optional. There is no need to convert existing code to use TFRecords, unless you are using [`tf.data`](https://www.tensorflow.org/guide/datasets) and reading data is still the bottleneck to training. See [Data Input Pipeline Performance](https://www.tensorflow.org/guide/performance/datasets) for dataset performance tips.\n",
        "Catatan: Meskipun bermanfaat, struktur ini opsional. Tidak perlu untuk mengubah kode yang ada untuk menggunakan TFRecords, kecuali anda menggunakan [`tf.data`](https://www.tensorflow.org/guide/datasets) dan membaca data masih menyebabkan kemacetan pada saat training. Lihat [Data Input Pipeline Performance](https://www.tensorflow.org/guide/performance/datasets) untuk kiat kinerja dataset."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WkRreBf1eDVc"
      },
      "source": [
        "## Pengaturan"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Ja7sezsmnXph"
      },
      "outputs": [],
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "try:\n",
        "  # %tensorflow_version hanya ada di Colab\n",
        "  !pip install tf-nightly\n",
        "except Exception:\n",
        "  pass\n",
        "import tensorflow as tf\n",
        "\n",
        "import numpy as np\n",
        "import IPython.display as display"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e5Kq88ccUWQV"
      },
      "source": [
        "## `tf.Example`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VrdQHgvNijTi"
      },
      "source": [
        "### Tipe data untuk `tf.Example`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lZw57Qrn4CTE"
      },
      "source": [
        "Pada dasarnya, `tf.Example` adalah pemetaan `{\"string\": tf.train.Feature}`\n",
        "\n",
        "Tipe pesan `tf.train.Feature` dapat menerima salah satu dari tiga tipe berikut (Sebagai referensi lihat [`.proto` file](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/feature.proto)). Sebagian besar tipe ini dapat diubah menjadi salah satu dari berikut:\n",
        "\n",
        "\n",
        "1. `tf.train.BytesList` (tipe berikut dapat diubah)\n",
        "\n",
        "  - `string`\n",
        "  - `byte`\n",
        "\n",
        "1. `tf.train.FloatList` (tipe berikut dapat diubah)\n",
        "\n",
        "  - `float` (`float32`)\n",
        "  - `double` (`float64`)\n",
        "\n",
        "1. `tf.train.Int64List` (tipe berikut dapat diubah)\n",
        "\n",
        "  - `bool`\n",
        "  - `enum`\n",
        "  - `int32`\n",
        "  - `uint32`\n",
        "  - `int64`\n",
        "  - `uint64`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "_e3g9ExathXP"
      },
      "source": [
        "Untuk mengkonversi tipe standar Tensorflow menjadi `tf.Example`- `tf.train.Feature` kompatibel, Anda dapat menggunakan fungsi pintas dibawah ini. Perhatikan bahwa setiap fungsi mengambil inputan berupa nilai skalar dan mengembalikan `tf.train.Feature` yang berisi salah satu dari tiga tipe `list` diatas:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "mbsPOUpVtYxA"
      },
      "outputs": [],
      "source": [
        "# Fungsi Berikut dapat digunakan untuk mengkonversi sebuah nilau menjadi tipe yang kompatibel\n",
        "# dengan tf.Example.\n",
        "def _bytes_feature(value):\n",
        "  \"\"\"Returns a bytes_list from a string / byte.\"\"\"\n",
        "    if isinstance(value, type(tf.constant(0))):\n",
        "    value = value.numpy() # BytesList tidak membuka string dari EagerTensor.\n",
        "  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))\n",
        "\n",
        "def _float_feature(value):\n",
        "  \"\"\"Returns a float_list from a float / double.\"\"\"\n",
        "  return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))\n",
        "\n",
        "def _int64_feature(value):\n",
        "  \"\"\"Returns an int64_list from a bool / enum / int / uint.\"\"\"\n",
        "  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Wst0v9O8hgzy"
      },
      "source": [
        "Catatan: Agar tetap sederhana, contoh ini hanya menggunakan input skalar. Cara yang paling mudah untuk menangani fitur yang bukan skalar adalah dengan menggunakan `tf.serialize_tensor` untuk mengkonversi tensors menjadi string biner. String adalah skalar di dalam tensorflow. Gunakan `tf.parse_tensor` untuk mengkonversi string biner kembali ke tensor."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vsMbkkC8xxtB"
      },
      "source": [
        "Dibawah ini beberapa contoh bagaimana fungsi-fungsi tersebut bekerja. Perhatikan berbagai jenis tipe input dan dan tipe output terstandardisasi. Jika tipe input untuk fungsi tidak cocok dengan salah satu tipe yang dapat diubah di atas, fungsi akan menampilkan eksepsi (contoh: `_int64_feature(1.0)` akan menghsailkan error, karena `1.0` bertipe float, sehingga seharusnya digunakan fungsi `_float_feature`):"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "hZzyLGr0u73y"
      },
      "outputs": [],
      "source": [
        "print(_bytes_feature(b'test_string'))\n",
        "print(_bytes_feature(u'test_bytes'.encode('utf-8')))\n",
        "\n",
        "print(_float_feature(np.exp(1)))\n",
        "\n",
        "print(_int64_feature(True))\n",
        "print(_int64_feature(1))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nj1qpfQU5qmi"
      },
      "source": [
        "Semua pesan proto dapat diserialisasi menjadi string biner menggunakan *method* `.SerializeToString`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5afZkORT5pjm"
      },
      "outputs": [],
      "source": [
        "feature = _float_feature(np.exp(1))\n",
        "\n",
        "feature.SerializeToString()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "laKnw9F3hL-W"
      },
      "source": [
        "### Membuat pesan `tf.Example` "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b_MEnhxchQPC"
      },
      "source": [
        "Misalkan anda ingin membuat pesan `tf.Example` dari data yang ada. Dalam praktiknya, dataset dapat datang dari mana saja, tetapi prosedur dalam pembuatan pesan `tf.Example` dari sebuah pengamatan akan sama:\n",
        "\n",
        "1. Dalam setiap pengamatan, setiap nilai harus dikonversi menjadi sebuah `tf.train.Feature` berisi salah satu 3 tipe kompatibel, menggunakan satu dari fungsi-fungsi diatas.\n",
        "\n",
        "1. Anda membuat peta (*dictionary*) dari string nama fitur menjadi nilai fitur yang dikodekan yang diproduksi pada #1.\n",
        "\n",
        "1. Peta yang diproduksi pada langakah 2 dikonversi menjadi [`Features` message](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/feature.proto#L85)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4EgFQ2uHtchc"
      },
      "source": [
        "Dalam notebook ini, Anda akan membuat dataset menggunakan NumPy\n",
        "\n",
        "Dataset ini memilik 4 fitur:\n",
        "\n",
        "* fitur boolean, `False` atau `True` dengan probabilitas yang sama\n",
        "* fitur integer secara berdistrbusi uniform dan acak dipilih dari nilai `[0, 5]`\n",
        "* fitur string dibuat dari tabel string menggunakan fitur integer sebagai index\n",
        "* fitur float dari berdistribusi normal standar\n",
        "\n",
        "Pertimbangkan sampel berisi 10,000 pengamatan independen dan terdistribusi secara identik dari setiap distribusi di atas:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CnrguFAy3YQv"
      },
      "outputs": [],
      "source": [
        "# Jumlah observasi di dalam dataset\n",
        "n_observations = int(1e4)\n",
        "\n",
        "# Fitur Boolean, dikodekan sebagai False atau True.\n",
        "feature0 = np.random.choice([False, True], n_observations)\n",
        "\n",
        "# Fitur integer, acak dari 0 sampai 4.\n",
        "feature1 = np.random.randint(0, 5, n_observations)\n",
        "\n",
        "# Fitur string\n",
        "strings = np.array([b'cat', b'dog', b'chicken', b'horse', b'goat'])\n",
        "feature2 = strings[feature1]\n",
        "\n",
        "# Fitur float, dari distribusi normal standar\n",
        "feature3 = np.random.randn(n_observations)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aGrscehJr7Jd"
      },
      "source": [
        "Setiap fitur ini dapat diubah menjadi sebuah tipe kompatibel `tf.Example`- menggunakan salah satu dari `_bytes_feature`, `_float_feature`, `_int64_feature`. Anda kemudian dapat membuat pesan `tf.Example` dari fitur yang dikodekan ini:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "RTCS49Ij_kUw"
      },
      "outputs": [],
      "source": [
        "def serialize_example(feature0, feature1, feature2, feature3):\n",
        "  \"\"\"\n",
        "  Creates a tf.Example message ready to be written to a file.\n",
        "  \"\"\"\n",
        "  # Membuat *dictionary* yang memetakan fitur nama ke tipe data kompatible tf.Example\n",
        "  feature = {\n",
        "      'feature0': _int64_feature(feature0),\n",
        "      'feature1': _int64_feature(feature1),\n",
        "      'feature2': _bytes_feature(feature2),\n",
        "      'feature3': _float_feature(feature3),\n",
        "  }\n",
        "\n",
        "  # Membuat pesan Fitur menggunakan tf.train.Example.\n",
        "\n",
        "  example_proto = tf.train.Example(features=tf.train.Features(feature=feature))\n",
        "  return example_proto.SerializeToString()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XftzX9CN_uGT"
      },
      "source": [
        "Sebagai contoh, misalkan anda mempunyai pengamatan dari dataset, `[False, 4, bytes('goat'), 0.9876]`. Anda dapat membuat dan menampilkan pesan `tf.Example` untuk pengmatan ini menggunakan `create_message()`. Setiap pengamatan akan ditulis sebagai pesan `Features` seperti diatas. Perhatikan bahwa `tf.Example` [message](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto#L88) adalah sebuah pembungkus di sekeliling pesan `Features`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "N8BtSx2RjYcb"
      },
      "outputs": [],
      "source": [
        "# Ini adalah contoh pengamatan dari dataset.\n",
        "\n",
        "example_observation = []\n",
        "\n",
        "serialized_example = serialize_example(False, 4, b'goat', 0.9876)\n",
        "serialized_example"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "_pbGATlG6u-4"
      },
      "source": [
        "Untuk melakukan dekode gunakan *method* `tf.train.Example.FromString`"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "dGim-mEm6vit"
      },
      "outputs": [],
      "source": [
        "example_proto = tf.train.Example.FromString(serialized_example)\n",
        "example_proto"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "o6qxofy89obI"
      },
      "source": [
        "## Detail Format TFRecords\n",
        "\n",
        "file TFRecord berisi sebuah urutan dari record. File hanya dapat dibaca secara berurutan.\n",
        "\n",
        "Setiap record berisi byte-string, untuk data-payload, ditambah data-length, dan hash CRC32C (32-bit CRC menggunakan Castagnoli polynomial) untuk mengecek integritas.\n",
        "\n",
        "Setiap record disimpan dalam format berikut:\n",
        "\n",
        "    uint64 length\n",
        "    uint32 masked_crc32_of_length\n",
        "    byte   data[length]\n",
        "    uint32 masked_crc32_of_data\n",
        "\n",
        "Record digabungkan untuk memproduksi file.  CRCs \n",
        "[dijelaskan disini](https://en.wikipedia.org/wiki/Cyclic_redundancy_check), dan *mask* dari CRC adalah:\n",
        "\n",
        "    masked_crc = ((crc >> 15) | (crc << 17)) + 0xa282ead8ul\n",
        "\n",
        "Catatan: Tidak ada syarat untuk menggunakan `tf.Example` pada file TFRecord. `tf.Example` adalah *method* untuk proses serialisasi *dictionary* menjadi byte-string. Baris teks, data enkode gambar, atau tensor terserialisasi (menggunakan `tf.io.serialize_tensor`, dan `tf.io.parse_tensor` when loading). See the `tf.io` module for more options. `tf.io.parse_tensor` ketika memuat). Lihat modul `tf.io` untuk opsi lebih."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "y-Hjmee-fbLH"
      },
      "source": [
        "## File TFRecord menggunakan `tf.data`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GmehkCCT81Ez"
      },
      "source": [
        "Modul `tf.data` juga menyediakan alat untuk membaca dan menulis data di TensorFlow."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1FISEuz8ubu3"
      },
      "source": [
        "### Menulis file TFRecord\n",
        "\n",
        "Cara termudah untuk mendapatkan data menjadi dataset adalah menggunakan *method* `from_tensor_slices`.\n",
        "\n",
        "Digunakan pada array, *method* ini mengembalikan dataset skalar:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "mXeaukvwu5_-"
      },
      "outputs": [],
      "source": [
        "tf.data.Dataset.from_tensor_slices(feature1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "f-q0VKyZvcad"
      },
      "source": [
        "Digunakan pada sebuah array tuple, mengembalikan dataset tuple:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "H5sWyu1kxnvg"
      },
      "outputs": [],
      "source": [
        "features_dataset = tf.data.Dataset.from_tensor_slices((feature0, feature1, feature2, feature3))\n",
        "features_dataset"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "m1C-t71Nywze"
      },
      "outputs": [],
      "source": [
        "# Gunakan `take(1)` untuk mengambil satu contoh dari dataset.\n",
        "for f0,f1,f2,f3 in features_dataset.take(1):\n",
        "  print(f0)\n",
        "  print(f1)\n",
        "  print(f2)\n",
        "  print(f3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mhIe63awyZYd"
      },
      "source": [
        "Gunakan *method* `tf.data.Dataset.map` untuk menggunakan fungsi ke setiap elemen sebuah `Dataset`.\n",
        "\n",
        "Fungsi yang terpetakan harus beroprasi dalam mode grafik TensorFlow —fungsi beroperasi dan mengembalikan `tf.Tensors`. Fungsi non-tensor, seperti `serialize_example`, dapat dibungkus dengan `tf.py_function` agar menjadi kompatibel.\n",
        "\n",
        "Menggunakan `tf.py_function` memerlukan penentuan bentuk dan tipe informasi yang tidak tersedia:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "apB5KYrJzjPI"
      },
      "outputs": [],
      "source": [
        "def tf_serialize_example(f0,f1,f2,f3):\n",
        "  tf_string = tf.py_function(\n",
        "    serialize_example,\n",
        "    (f0,f1,f2,f3),  # berikan args ini ke fungsi yang di atas.\n",
        "    tf.string)      # tipe yang dikembalikan adalah `tf.string`.\n",
        "  return tf.reshape(tf_string, ()) # Hasilnya adalah skalar"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "lHFjW4u4Npz9"
      },
      "outputs": [],
      "source": [
        "tf_serialize_example(f0,f1,f2,f3)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CrFZ9avE3HUF"
      },
      "source": [
        "Gunakan fungsi ini ke setiap elemen di dataset:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VDeqYVbW3ww9"
      },
      "outputs": [],
      "source": [
        "serialized_features_dataset = features_dataset.map(tf_serialize_example)\n",
        "serialized_features_dataset"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DlDfuh46bRf6"
      },
      "outputs": [],
      "source": [
        "def generator():\n",
        "  for features in features_dataset:\n",
        "    yield serialize_example(*features)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "iv9oXKrcbhvX"
      },
      "outputs": [],
      "source": [
        "serialized_features_dataset = tf.data.Dataset.from_generator(\n",
        "    generator, output_types=tf.string, output_shapes=())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Dqz8C4D5cIj9"
      },
      "outputs": [],
      "source": [
        "serialized_features_dataset"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "p6lw5VYpjZZC"
      },
      "source": [
        "Dan tulis ke dalam TFRecord file:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vP1VgTO44UIE"
      },
      "outputs": [],
      "source": [
        "filename = 'test.tfrecord'\n",
        "writer = tf.data.experimental.TFRecordWriter(filename)\n",
        "writer.write(serialized_features_dataset)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6aV0GQhV8tmp"
      },
      "source": [
        "### Membaca file TFRecord"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "o3J5D4gcSy8N"
      },
      "source": [
        "Anda juga dapat membaca file TFRecord menggunakan kelas `tf.data.TFRecordDataset`.\n",
        "\n",
        "Informasi lebih lanjut tentang penggunaan file TFRecord menggunakan `tf.data` dapat ditemukan [disini](https://www.tensorflow.org/guide/datasets#consuming_tfrecord_data).\n",
        "\n",
        "Menggunakan `TFRecordDataset`  berguna untuk melakukan standarisasi data input dan mengoptimasi performa."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6OjX6UZl-bHC"
      },
      "outputs": [],
      "source": [
        "filenames = [filename]\n",
        "raw_dataset = tf.data.TFRecordDataset(filenames)\n",
        "raw_dataset"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6_EQ9i2E_-Fz"
      },
      "source": [
        "Pada titik ini dataset berisi pesan `tf.train.Example` yang terserialisasi. Ketika diiterasi akan mengembalikan skalar tensor string.\n",
        "\n",
        "Gunakan *method* `.take` untuk menampilkan hanya 10 record pertama.\n",
        "\n",
        "Catatan: proses iterasi `tf.data.Dataset` hanya bekerja ketika *eager execution* diaktifkan."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "hxVXpLz_AJlm"
      },
      "outputs": [],
      "source": [
        "for raw_record in raw_dataset.take(10):\n",
        "  print(repr(raw_record))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W-6oNzM4luFQ"
      },
      "source": [
        "Tensor-tensor ini dapat diuraikan menggunakan fungsi dibawah. Perhatikan bahwa `feature_description` disini dibutuhkan karena dataset menggunakan *graph-execution*, dan memerlukan deskripsi ini untuk membangun bentuk dan tipe asli mereka:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zQjbIR1nleiy"
      },
      "outputs": [],
      "source": [
        "# Membuat deskripsi dari fitur.\n",
        "feature_description = {\n",
        "    'feature0': tf.io.FixedLenFeature([], tf.int64, default_value=0),\n",
        "    'feature1': tf.io.FixedLenFeature([], tf.int64, default_value=0),\n",
        "    'feature2': tf.io.FixedLenFeature([], tf.string, default_value=''),\n",
        "    'feature3': tf.io.FixedLenFeature([], tf.float32, default_value=0.0),\n",
        "}\n",
        "\n",
        "def _parse_function(example_proto):\n",
        "  # Uraikan input proto `tf.Example` menggunkan dictionary di atas.\n",
        "  return tf.io.parse_single_example(example_proto, feature_description)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gWETjUqhEQZf"
      },
      "source": [
        "Dengan cara lain, gunakan `tf.parse example` untuk menguraikan seluruh batch sekaligus. Gunakan fungsi ini ke setiap item dalam dataset menggunakan *mehod* `tf.data.Dataset.map`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6Ob7D-zmBm1w"
      },
      "outputs": [],
      "source": [
        "parsed_dataset = raw_dataset.map(_parse_function)\n",
        "parsed_dataset"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sNV-XclGnOvn"
      },
      "source": [
        "Gunakan *eager execution* untuk menampilkan pengamatan pada dataset. Ada 10,000 pengamatan pada dataset ini, tetapi Anda hanya akan menampilkan 10 data pertama. Data ditampilkan sebagai sebuah *dictionary* dari fitur. Setiap item adalah `tf.Tensor`, dan elemen `numpy` pada tensor ini menampilkan nilai dari fitur:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "x2LT2JCqhoD_"
      },
      "outputs": [],
      "source": [
        "for parsed_record in parsed_dataset.take(10):\n",
        "  print(repr(parsed_record))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Cig9EodTlDmg"
      },
      "source": [
        "Di sini, fungsi `tf.parse_example` membuka *field* `tf.Example` menjadi tensor standar."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jyg1g3gU7DNn"
      },
      "source": [
        "## File TFRecord di Python"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3FXG3miA7Kf1"
      },
      "source": [
        "Modul `tf.io` juga berisikan fungsi Python murni untuk membaca dan menulis file-file TFRecord."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CKn5uql2lAaN"
      },
      "source": [
        "### Menulis file TFRecord"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LNW_FA-GQWXs"
      },
      "source": [
        "Selanjutnya, tuliskan 10,000 pengamatan ke file `test.tfrecord`. Setiap pengamatan dikonversi menjadi sebuah pesan `tf.Example`, kemudian ditulis dalam file. Anda kemudian dapat memeriksa apakah file `test.tfrecord` berhasil dibuat:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "MKPHzoGv7q44"
      },
      "outputs": [],
      "source": [
        "# Menulis pengamatan `tf.Example` kedalam file\n",
        "with tf.io.TFRecordWriter(filename) as writer:\n",
        "  for i in range(n_observations):\n",
        "    example = serialize_example(feature0[i], feature1[i], feature2[i], feature3[i])\n",
        "    writer.write(example)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EjdFHHJMpUUo"
      },
      "outputs": [],
      "source": [
        "!du -sh {filename}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2osVRnYNni-E"
      },
      "source": [
        "### Membaca file TFRecord\n",
        "\n",
        "Tensor yang terserialisasi ini dapat diurai menggunakan `tf.train.Example.ParseFromString`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "U3tnd3LerOtV"
      },
      "outputs": [],
      "source": [
        "filenames = [filename]\n",
        "raw_dataset = tf.data.TFRecordDataset(filenames)\n",
        "raw_dataset"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nsEAACHcnm3f"
      },
      "outputs": [],
      "source": [
        "for raw_record in raw_dataset.take(1):\n",
        "  example = tf.train.Example()\n",
        "  example.ParseFromString(raw_record.numpy())\n",
        "  print(example)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "S0tFDrwdoj3q"
      },
      "source": [
        "## Panduan: Membaca dan menulis data gambar"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rjN2LFxFpcR9"
      },
      "source": [
        "Ini adalah contoh proses dari ujung ke ujung bagaimana cara membaca dan menulis data gambar menggunakan TFRecord. Menggunakan gambar sebagai input data, Anda akan menulis data sebagai file TFRecord, kemudian membaca file dan menampilkan gambar tersebut.\n",
        "\n",
        "Hal ini dapat berguna jika, misalnya, Anda ingin menggunakan berbagai model dengan input dataset yang sama. Dibandingkan menyimpan data gambar secara mentah, data dapat diproses terlebih dahulu menjadi format TFRecord, dan dapat digunakan pada proses lebih lanjut dan pemodelan.\n",
        "\n",
        "Pertama, mari kita unduh [gambar](https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg) kucing di salju dan [foto](https://upload.wikimedia.org/wikipedia/commons/f/fe/New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg) dari Jembatan Williamsburg, NYC yang sedang dalam proses pembangunan."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5Lk2qrKvN0yu"
      },
      "source": [
        "### Mengambil gambar"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3a0fmwg8lHdF"
      },
      "outputs": [],
      "source": [
        "cat_in_snow  = tf.keras.utils.get_file('320px-Felis_catus-cat_on_snow.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/320px-Felis_catus-cat_on_snow.jpg')\n",
        "williamsburg_bridge = tf.keras.utils.get_file('194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/194px-New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7aJJh7vENeE4"
      },
      "outputs": [],
      "source": [
        "display.display(display.Image(filename=cat_in_snow))\n",
        "display.display(display.HTML('Image cc-by: <a \"href=https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg\">Von.grzanka</a>'))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "KkW0uuhcXZqA"
      },
      "outputs": [],
      "source": [
        "display.display(display.Image(filename=williamsburg_bridge))\n",
        "display.display(display.HTML('<a \"href=https://commons.wikimedia.org/wiki/File:New_East_River_Bridge_from_Brooklyn_det.4a09796u.jpg\">From Wikimedia</a>'))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VSOgJSwoN5TQ"
      },
      "source": [
        "### Menulis file TFRecord"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Azx83ryQEU6T"
      },
      "source": [
        "Sama seperti sebelumnya, lakukan enkode fitur sebagai tipe yang kompatibel dengan `tf.Example`. Hal ini akan menyimpan fitur string dari gambar raw, juga tinggi, lebar, kedalaman , dan fitur `label`. Yang terakhir digunakan ketika Anda menulis file untuk membedakan antara gambar kucing dan gambar jembatan. Gunakan `0` untuk gambar kucing, and `1` untuk gambar jembatan:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kC4TS1ZEONHr"
      },
      "outputs": [],
      "source": [
        "image_labels = {\n",
        "    cat_in_snow : 0,\n",
        "    williamsburg_bridge : 1,\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "c5njMSYNEhNZ"
      },
      "outputs": [],
      "source": [
        "# Ini sebuah contoh, hanya menggunakan gambar kucing.\n",
        "image_string = open(cat_in_snow, 'rb').read()\n",
        "\n",
        "label = image_labels[cat_in_snow]\n",
        "\n",
        "# Membuat dictionary dengan fitur yang mungkin relevan.\n",
        "def image_example(image_string, label):\n",
        "  image_shape = tf.image.decode_jpeg(image_string).shape\n",
        "\n",
        "  feature = {\n",
        "      'height': _int64_feature(image_shape[0]),\n",
        "      'width': _int64_feature(image_shape[1]),\n",
        "      'depth': _int64_feature(image_shape[2]),\n",
        "      'label': _int64_feature(label),\n",
        "      'image_raw': _bytes_feature(image_string),\n",
        "  }\n",
        "\n",
        "  return tf.train.Example(features=tf.train.Features(feature=feature))\n",
        "\n",
        "for line in str(image_example(image_string, label)).split('\\n')[:15]:\n",
        "  print(line)\n",
        "print('...')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2G_o3O9MN0Qx"
      },
      "source": [
        "Perhatikan semua fitur sekarang disimapan dalam pesan `tf.Example`. Selanjutnya, gunakan kode diatas dan menulis contoh pesan ke file yang diberi nama `images.tfrecords`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qcw06lQCOCZU"
      },
      "outputs": [],
      "source": [
        "# Tulis file gambar raw ke `images.tfrecords`.\n",
        "# Pertama, proses dua gambar ke dalam pesan `tf.Example`.\n",
        "# Kemudian, tulis ke file `.tfrecords`\n",
        "record_file = 'images.tfrecords'\n",
        "with tf.io.TFRecordWriter(record_file) as writer:\n",
        "  for filename, label in image_labels.items():\n",
        "    image_string = open(filename, 'rb').read()\n",
        "    tf_example = image_example(image_string, label)\n",
        "    writer.write(tf_example.SerializeToString())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yJrTe6tHPCfs"
      },
      "outputs": [],
      "source": [
        "!du -sh {record_file}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jJSsCkZLPH6K"
      },
      "source": [
        "### Membaca file TFRecord\n",
        "\n",
        "Anda sekarang mempunyai file `images.tfrecords` dan dapat melakukan iterasi terhadap record yang ada di dalamnya untuk membaca kembali apa yang telah Anda tulis. Mengingat dalam contoh ini anda hanya akan menghasilkan kembali gambar, satu-satunya fitur yang anda butuhkan adalah string dari gambar raw. Ekstrak string menggunakan getters yang telah dideskripsikan di atas, yaitu `example.features.feature['image_raw'].bytes_list.value[0]`. Anda juga dapat menggunakan label untuk menentukan record mana yang kucing dan record mana yang jembatan:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "M6Cnfd3cTKHN"
      },
      "outputs": [],
      "source": [
        "raw_image_dataset = tf.data.TFRecordDataset('images.tfrecords')\n",
        "\n",
        "# Membuat dictionary mendeskripsikan fitur\n",
        "image_feature_description = {\n",
        "    'height': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'width': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'depth': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'label': tf.io.FixedLenFeature([], tf.int64),\n",
        "    'image_raw': tf.io.FixedLenFeature([], tf.string),\n",
        "}\n",
        "\n",
        "def _parse_image_function(example_proto):\n",
        "  # Uraikan input proto tf.Example menggunakan dictionary diatas.\n",
        "  return tf.io.parse_single_example(example_proto, image_feature_description)\n",
        "\n",
        "parsed_image_dataset = raw_image_dataset.map(_parse_image_function)\n",
        "parsed_image_dataset"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0PEEFPk4NEg1"
      },
      "source": [
        "Memulihkan gambar-gambar dari file TFRecord:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "yZf8jOyEIjSF"
      },
      "outputs": [],
      "source": [
        "for image_features in parsed_image_dataset:\n",
        "  image_raw = image_features['image_raw'].numpy()\n",
        "  display.display(display.Image(data=image_raw))"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [
        "pL--_KGdYoBz"
      ],
      "name": "tfrecord.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
